/*
 *  Copyright 2004 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.webapp.struts;

import org.apache.struts.tiles.ComponentDefinition;
import org.apache.struts.tiles.xmlDefinition.XmlDefinition;

import java.util.*;

/**
 * <p>This class provides possibility to get name of parent tiles definition</p>
 * <p>Also gives ability to use containers.</p>
 * <p><a href="HeritableComponentDefinition.java.html"><i>View Source</i></a></p>
 * <p/>
 *
 * @author Andrey Grebnev <a href="mailto:andrey.grebnev@blandware.com">&lt;andrey.grebnev@blandware.com&gt;</a>
 * @author Roman Puchkovskiy <a href="mailto:roman.puchkovskiy@blandware.com">
 *         &lt;roman.puchkovskiy@blandware.com&gt;</a>
 * @version $Revision: 1.8 $ $Date: 2007/05/13 10:12:36 $
 */
public class HeritableComponentDefinition extends ComponentDefinition {

	/**
	 * Extends attribute value.
	 */
	private String inherit = null;

    /**
     * List of containers to which this definition may be put (each container
     * is represented with full list identifier)
     */
    protected List containersList = new ArrayList();

    /**
     * Description
     */
    protected String description = null;

    /**
     * Whether component can be contained different containers at the same
     * time
     */
    protected boolean copyable = true;

    /**
     * Default constructor.
     */
    public HeritableComponentDefinition() {
    }

    /**
     * Copy constructor.
     *
     * @param definition definition to copy
     */
    public HeritableComponentDefinition(HeritableComponentDefinition definition) {
        this((ComponentDefinition) definition);
        inherit = definition.inherit;
        containersList = new ArrayList(definition.containersList);
        description = definition.description;
    }

    /**
	 * Copy Constructor.
	 * Creates a new definition initialized with parent definition.
	 * Does a deep copy.
     *
     * @param definition definition from which to init this one
     */
	public HeritableComponentDefinition(ComponentDefinition definition) {
		super(definition);
        // Make a deep copy of attributes
        Map newAttributes = new HashMap();
        Iterator i = attributes.entrySet().iterator();
        while (i.hasNext()) {
            Map.Entry entry = (Map.Entry) i.next();
            Object key = entry.getKey();
            Object value = entry.getValue();
            if (value instanceof List) {
                value = copyList((List) value);
            }
            newAttributes.put(key, value);
        }
        attributes = newAttributes;
    }

    /**
	 * Constructor.
	 * Creates a new definition initialized from a RawDefinition.
	 * Raw definitions are used to read definition from a data source (xml file, db, ...).
	 * A RawDefinition mainly contains properties of type String, while Definition
	 * contains more complex type (ex : Controller).
	 * Do a shallow copy : attributes are shared between objects, but not the Map
	 * containing attributes.
	 * OO Design issues : Actually RawDefinition (XmlDefinition) extends ComponentDefinition.
	 * This must not be the case. I have do it because I am lazy.
     *
     * @param definition definition from which to init this one
     */
	public HeritableComponentDefinition(XmlDefinition definition) {
		this((ComponentDefinition) definition);
		setExtends(definition.getExtends());
	}

	/**
	 * Constructor.
	 * Creates a new definition initialized from a RawDefinition.
	 * Raw definitions are used to read definition from a data source (xml file, db, ...).
	 * A RawDefinition mainly contains properties of type String, while Definition
	 * contains more complex type (ex : Controller).
	 * Do a shallow copy : attributes are shared between objects, but not the Map
	 * containing attributes.
	 * OO Design issues : Actually RawDefinition (XmlDefinition) extends ComponentDefinition.
	 * This must not be the case. I have do it because I am lazy.
     *
     * @param definition definition from which to init this one
     */
	public HeritableComponentDefinition(ContainedXmlDefinition definition) {
		this((XmlDefinition) definition);
        setContainersList(definition.getContainersList());
        setDescription(definition.getDescription());
        setCopyable(definition.isCopyable());
    }

    /**
	 * Sets name of definition that is extented by this one
	 *
	 * @param name Name of the extended definition.
	 */
	public void setExtends(String name) {
		inherit = name;
	}

	/**
	 * Gets name of definition that is extented by this one
	 *
	 * @return Name of the extended definition.
	 */
	public String getExtends() {
		return inherit;
	}

	/**
	 * Gets whether this definition extends some other definition or not
     *
     * @return Whether this definition extends some other definition
	 */
	public boolean isExtending() {
		return inherit != null;
	}

    /**
     * Returns containers list.
     *
     * @return list of containers
     * @see #containersList
     */
    public List getContainersList() {
        return containersList;
    }

    /**
     * Sets containers list.
     *
     * @param containersList list of containers to set
     * @see #containersList
     */
    public void setContainersList(List containersList) {
        this.containersList = containersList;
    }

    /**
     * Returns description.
     *
     * @return description
     */
    public String getDescription() {
        return description;
    }

    /**
     * Sets description.
     *
     * @param description description to set
     */
    public void setDescription(String description) {
        this.description = description;
    }

    /**
     * Returns whether component can be contained different containers at the
     * same time.
     *
     * @return true if component can be copied
     */
    public boolean isCopyable() {
        return copyable;
    }

    /**
     * Sets whether component can be contained different containers at the
     * same time.
     *
     * @param copyable value to set
     */
    public void setCopyable(boolean copyable) {
        this.copyable = copyable;
    }

    /**
     * Does a deep copy of list - this is done resursively for each nested list.
     *
     * @param list list to copy
     * @return copy
     */
    protected List copyList(List list) {
        boolean isContainerList = list instanceof ContainerList;
        List newList = isContainerList
                ? new ContainerList((ContainerList) list)
                : new ArrayList();
        newList.clear();
        Iterator i = list.iterator();
        while (i.hasNext()) {
            Object elem = i.next();
            if (elem instanceof List) {
                elem = copyList((List) elem);
            }
            newList.add(elem);
        }
        return newList;
    }
}
